﻿using System.Collections.Generic;
using Ahresty.Entity;

namespace Ahresty.Algorithms
{
    /// <summary>
    /// 凹部の要素を操作するクラス
    /// </summary>
    internal class ConcaveHandler
    {
        #region Methods

        /// <summary>
        /// 指定の材料データから凹部になる要素を探し出す
        /// </summary>
        internal static Unit[] LookForConcaves(IN3DData in3dData)
        {
            List<Unit> concaves = new List<Unit>();

            for (short z = 0; z < in3dData.MaxZ; z++)
                for (short y = 0; y < in3dData.MaxY; y++)
                    for (short x = 0; x < in3dData.MaxX; x++)
                        if (ConcaveHandler.CheckConcave(in3dData, x, y, z))
                            concaves.Add(new Unit { X = x, Y = y, Z = z, });

            return concaves.ToArray();
        }

        /// <summary>
        /// 指定の材料データから凹部になる要素を探し出す（要素の数のみを返す）
        /// </summary>
        internal static int LookForConcaveCount(IN3DData in3dData)
        {
            int result = 0;

            for (short z = 0; z < in3dData.MaxZ; z++)
                for (short y = 0; y < in3dData.MaxY; y++)
                    for (short x = 0; x < in3dData.MaxX; x++)
                        if (ConcaveHandler.CheckConcave(in3dData, x, y, z))
                            result++;

            return result;
        }

        /// <summary>
        /// 指定の要素が凹部かどうかをチェックする
        /// </summary>
        internal static bool CheckConcave(IN3DData in3dData, int x, int y, int z)
        {
            byte value = in3dData.Data[x, y, z];

            byte[] surround = new byte[6];
            surround[0] = z > 0 ? in3dData.Data[x, y, z - 1] : (byte)' ';
            surround[1] = y > 0 ? in3dData.Data[x, y - 1, z] : (byte)' ';
            surround[2] = x > 0 ? in3dData.Data[x - 1, y, z] : (byte)' ';
            surround[3] = x + 1 == in3dData.MaxX ? (byte)' ' : in3dData.Data[x + 1, y, z];
            surround[4] = y + 1 == in3dData.MaxY ? (byte)' ' : in3dData.Data[x, y + 1, z];
            surround[5] = z + 1 == in3dData.MaxZ ? (byte)' ' : in3dData.Data[x, y, z + 1];

            if (value != surround[1] && surround[1] == surround[2] &&
                surround[2] == surround[3] && surround[3] == surround[4] && surround[4] == surround[5] &&
                surround[1] == in3dData.MaterialID)
                return true;

            if (value != surround[0] && surround[0] == surround[2] &&
                surround[2] == surround[3] && surround[3] == surround[4] && surround[4] == surround[5] &&
                surround[0] == in3dData.MaterialID)
                return true;

            if (value != surround[0] && surround[0] == surround[1] && surround[1] == surround[3] &&
                surround[3] == surround[4] && surround[4] == surround[5] &&
                surround[0] == in3dData.MaterialID)
                return true;

            if (value != surround[0] && surround[0] == surround[1] && surround[1] == surround[2] &&
                surround[2] == surround[4] && surround[4] == surround[5] &&
                surround[0] == in3dData.MaterialID)
                return true;

            if (value != surround[0] && surround[0] == surround[1] && surround[1] == surround[2] &&
                surround[2] == surround[3] && surround[3] == surround[5] &&
                surround[0] == in3dData.MaterialID)
                return true;

            if (value != surround[0] && surround[0] == surround[1] && surround[1] == surround[2] &&
                surround[2] == surround[3] && surround[3] == surround[4] &&
                surround[0] == in3dData.MaterialID)
                return true;

            return false;
        }

        /// <summary>
        /// 指定の凹部要素を修正する
        /// </summary>
        /// <param name="in3dData">修正対象となる材料データ</param>
        /// <param name="concave">凹部要素</param>
        internal static void FixConcave(ref IN3DData in3dData, Unit concave, byte fixMaterialID)
        {
            in3dData.Data[concave.X, concave.Y, concave.Z] = fixMaterialID;
        }

        #endregion
    }
}